package com.nervenets.web.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.nervenets.Global;
import com.nervenets.i18n.Translator;
import com.nervenets.utils.HttpTools;
import com.nervenets.utils.NumberUtil;
import com.nervenets.utils.PropertiesLoader;
import com.nervenets.web.entity.KeyValue;
import com.nervenets.web.entity.ResponseResult;
import com.nervenets.web.rabbitmq.MessageProducer;
import com.nervenets.web.redis.GlobalService;
import com.nervenets.web.redis.GlobalVariablesService;
import com.nervenets.web.redis.utils.RedisSyncLocker;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public abstract class BaseController {
    protected static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    @Resource
    protected WxPayService wxPayService;
    @Resource
    protected MessageProducer messageProducer;
    @Resource
    protected GlobalService globalService;
    @Resource
    protected GlobalVariablesService globalVariablesService;
    @Resource
    protected RedisSyncLocker redisSyncLocker;

    protected ResponseResult successMessage() {
        return this.successMessage(null);
    }

    protected ResponseResult successMessage(Object object) {
        ResponseResult responseResult = ResponseResult.success();
        responseResult.setData(object);
        responseResult.setVersion("1.0");
        responseResult.setMessage(Translator.translate("code_200"));
        return responseResult;
    }

    @SafeVarargs
    protected final ResponseResult successMessage(Object object, KeyValue... keyValues) {
        ResponseResult responseResult = ResponseResult.success();
        responseResult.setData(object);
        responseResult.setVersion("1.0");
        responseResult.setMessage(Translator.translate("code_200"));
        for (KeyValue v : keyValues) {
            responseResult.put(v.getKey(), v.getValue());
        }
        return responseResult;
    }

    protected ResponseResult errorMessage(int messageCode) {
        return errorMessage(messageCode, Translator.translate("code_" + messageCode));
    }

    protected ResponseResult errorMessage(int messageCode, Object object) {
        ResponseResult responseResult = ResponseResult.failed();
        responseResult.setStatus(messageCode);
        responseResult.setData(object);
        responseResult.setVersion("1.0");
        responseResult.setMessage(Translator.translate("code_" + messageCode));
        return responseResult;
    }

    protected ResponseResult errorMessage(int messageCode, String customMessage) {
        ResponseResult responseResult = ResponseResult.failed();
        responseResult.setStatus(messageCode);
        responseResult.setData(null);
        responseResult.setVersion("1.0");
        responseResult.setMessage(customMessage);
        return responseResult;
    }

    protected ResponseResult errorMessage(int messageCode, Object object, String customMessage) {
        ResponseResult responseResult = ResponseResult.failed();
        responseResult.setStatus(messageCode);
        responseResult.setData(object);
        responseResult.setVersion("1.0");
        responseResult.setMessage(customMessage);
        return responseResult;
    }

    protected <T> ResponseResult errorMessage(Set<ConstraintViolation<T>> errors) {
        ResponseResult responseResult = ResponseResult.failed();
        responseResult.setVersion("1.0");
        List<KeyValue> err = new ArrayList<>();
        for (ConstraintViolation error : errors) {
            err.add(new KeyValue(error.getPropertyPath().toString(), error.getMessage()));
        }
        responseResult.setData(err);
        responseResult.setMessage((String) err.get(0).value);
        return responseResult;
    }

    protected ResponseResult aliPay(String subject, int money, String orderNumber, String notifyUrl, HttpServletRequest request) {
        final PropertiesLoader properties = Global.getApplicationProperties();
        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", properties.getProperty("alipay.app.id"), properties.getProperty("alipay.app.private.key"), "json", "utf8", properties.getProperty("alipay.app.public.key"), "RSA2");
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest appPayRequest = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(subject);
        model.setOutTradeNo(orderNumber);
        model.setTimeoutExpress("30m");
        model.setTotalAmount(String.valueOf(NumberUtil.fen2Yuan(money)));
        model.setProductCode("QUICK_MSECURITY_PAY");
        appPayRequest.setBizModel(model);
        appPayRequest.setNotifyUrl(notifyUrl);
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(appPayRequest);
            System.out.println(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。
            return successMessage(response.getBody());
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected WxPayMpOrderResult wxPay(String body, String orderNumber, long money, String openId, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.JSAPI)
                .openid(openId)
                .build();

        return wxPayService.createOrder(unifiedOrderRequest);
    }

    protected WxPayRefundResult wxRefund(String tradeNo, int amount) throws WxPayException {
        WxPayRefundRequest refundRequest = WxPayRefundRequest.newBuilder()
                .outRefundNo(tradeNo)
                .outTradeNo(tradeNo)
                .refundFee(amount)
                .totalFee(amount)
                .build();
        return wxPayService.refund(refundRequest);
    }

    protected String saveQrCode(byte[] qrcodeBytes, long id) {
        final PropertiesLoader propertiesLoader = Global.getApplicationProperties();
        StringBuilder buffer = new StringBuilder();
        buffer.append(propertiesLoader.getProperty("api.upload.folder")).append(File.separatorChar).append("upload").append(File.separatorChar).append("qrCode").append(File.separatorChar);
        File dir = new File(buffer.toString());
        if (!dir.exists()) {
            dir.mkdirs();
        }
        buffer.append(id).append(".png");

        try {
            FileOutputStream out = new FileOutputStream(buffer.toString());
            out.write(qrcodeBytes);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "https://" + propertiesLoader.getProperty("api.upload.domain") + "/upload/qrCode/" + id + ".png";
    }
}
